#include <assert.h>
#include <memory.h>
#include "hrd_bitmask.h"


bool HrdBitmask_IsGoalAchieved(hrd_bitmask_t goal, hrd_bitmask_list_t hrd_bitmask_list)
{
    return (hrd_bitmask_list[MAIN_OBJECT_INDEX] ^ goal) == 0;
}


hrd_bitmask_t HrdBitmask_GenerateNewObjectBitmask(hrd_bitmask_t hrd_object_bitmask, hrd_object_move_direction_et hrd_object_direction, uint8_t length)
{
    switch (hrd_object_direction) {
        case MOVE_UP:
            return hrd_object_bitmask << 8 * length;
        case MOVE_DOWN:
            return hrd_object_bitmask >> 8 * length;
        case MOVE_LEFT:
            return hrd_object_bitmask << 1 * length;
        case MOVE_RIGHT:
            return hrd_object_bitmask >> 1 * length;
        default:
            __assert_fail("Direction in {UP, DOWN, LEFT, RIGHT}", __FILE__, __LINE__, __func__);
            return hrd_object_bitmask;
    }
}


bool HrdBitmask_CanUpdateObject(hrd_bitmask_list_t hrd_bitmask_list, uint8_t hrd_object_index, hrd_object_move_direction_et hrd_object_direction, uint8_t length)
{
    assert(hrd_object_index != MAP_BITMASK_INDEX);
    assert(hrd_object_index < HRD_BITMASK_LIST_LENGTH);

    return (
          (hrd_bitmask_list[MAP_BITMASK_INDEX] ^ hrd_bitmask_list[hrd_object_index])
        & HrdBitmask_GenerateNewObjectBitmask(hrd_bitmask_list[hrd_object_index], hrd_object_direction, length)
    ) == 0;
}


void HrdBitmask_UpdateObject(hrd_bitmask_list_t hrd_bitmask_list, uint8_t hrd_object_index, hrd_object_move_direction_et hrd_object_direction, uint8_t length)
{
    assert(hrd_object_index != MAP_BITMASK_INDEX);
    assert(hrd_object_index < HRD_BITMASK_LIST_LENGTH);

    hrd_bitmask_t new_object_bitmask = HrdBitmask_GenerateNewObjectBitmask(hrd_bitmask_list[hrd_object_index], hrd_object_direction, length);

    hrd_bitmask_list[MAP_BITMASK_INDEX] ^= hrd_bitmask_list[hrd_object_index];
    hrd_bitmask_list[MAP_BITMASK_INDEX] |= new_object_bitmask;
    hrd_bitmask_list[hrd_object_index] = new_object_bitmask;
}

void HrdBitmask_InitMap(hrd_bitmask_list_t hrd_bitmask_list)
{
    uint8_t i;

    for (i = MAP_BITMASK_INDEX + 1; i < HRD_BITMASK_LIST_LENGTH; i++) {
        hrd_bitmask_list[MAP_BITMASK_INDEX] |= hrd_bitmask_list[i];
    }
}


void HrdBitmask_InitBoundary(hrd_bitmask_list_t hrd_bitmask_list)
{
    memset(hrd_bitmask_list, 0, sizeof(hrd_bitmask_list_t));
    hrd_bitmask_list[MAP_BITMASK_INDEX] = DEFAULT_BOUNDARY_BITMASK;
}


void HrdBitmask_SetBoundary(hrd_bitmask_list_t hrd_bitmask_list, hrd_bitmask_t boundary)
{
    hrd_bitmask_list[MAP_BITMASK_INDEX] |= boundary;
}


hrd_bitmask_t HrdBitmask_GetBoundary(hrd_bitmask_list_t hrd_bitmask_list)
{
    uint8_t i;
    hrd_bitmask_t boundary = hrd_bitmask_list[MAP_BITMASK_INDEX];

    for (i = MAP_BITMASK_INDEX + 1; i < HRD_BITMASK_LIST_LENGTH; i++) {
        boundary ^= hrd_bitmask_list[i];
    }

    return boundary;
}


bool HrdBitmask_IsGoalValid(hrd_bitmask_list_t hrd_bitmask_list, hrd_bitmask_t goal)
{
    return (hrd_bitmask_list[MAP_BITMASK_INDEX] & goal) == 0;
}


bool HrdBitmask_IsBitmaskListValid(hrd_bitmask_list_t hrd_bitmask_list)
{
    uint8_t i;
    hrd_bitmask_t mask = 0;

    for (i = 0; i < HRD_BITMASK_LIST_LENGTH; i++) {
        if (mask & hrd_bitmask_list[i]) {
            return false;
        }

        mask |= hrd_bitmask_list[i];
    }

    return true;
}